/**
 * 
 */
package util;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import javax.swing.AbstractListModel;

/**
 * A ListModel based around a user supplied list. All methods in this class call
 * the equivalent method in the underlying list.
 * 
 *  For updates to function properly all List methods must be called on this not 
 *  the underlying list.
 * 
 * @author Joshua Lockerman
 * @param <E> the type of element that the ListModel contains.
 *
 */
public class ForwardingListModel<E> extends AbstractListModel<E> implements List<E> {
    
    private static final long serialVersionUID = 1L;
    private final List<E> underlyingList;
    
    /**
     * Creates a ListModel based off a given underlying collection
     * @param underlyingList the collection this model will forward to.
     */
    public ForwardingListModel(List<E> underlyingList)
    {
        this.underlyingList = underlyingList;
    }
    
    @Override
    public int getSize()
    {
        return underlyingList.size();
    }
    
    @Override
    public E getElementAt(int index)
    {
        return underlyingList.get(index);
    }
    
    @Override
    public int size()
    {
        return underlyingList.size();
    }
    
    @Override
    public boolean isEmpty()
    {
        return underlyingList.isEmpty();
    }
    
    @Override
    public boolean contains(Object o)
    {
        return underlyingList.contains(o);
    }
    
    @Override
    public Iterator<E> iterator()
    {
        return underlyingList.iterator();
    }
    
    @Override
    public Object[] toArray()
    {
        return underlyingList.toArray();
    }
    
    @Override
    public <T> T[] toArray(T[] a)
    {
        return underlyingList.toArray(a);
    }
    
    @Override
    public boolean add(E e)
    {
        boolean added = underlyingList.add(e);
        int index1 = getSize();
        int index0 = index1 - 1;
        fireIntervalAdded(this, index0, index1);
        return added;
    }
    
    @Override
    public boolean remove(Object o)
    {
        int index = underlyingList.indexOf(o);
        if(index != -1) {
            underlyingList.remove(index);
            fireIntervalRemoved(this, index, index + 1);
        }
        return false;
    }
    
    @Override
    public boolean containsAll(Collection<?> c)
    {
        return underlyingList.containsAll(c);
    }
    
    @Override
    public boolean addAll(Collection<? extends E> c)
    {
        int index0 = getSize();
        if(underlyingList.addAll(c)) {
            int index1 = getSize();
            fireIntervalAdded(this, index0, index1);
            return true;
        }
        return false;
    }
    
    @Override
    public boolean addAll(int index, Collection<? extends E> c)
    {
        int index0 = index;
        int oldSize = getSize();
        if(underlyingList.addAll(index, c)) {
            int index1 = getSize() - oldSize + index;
            fireIntervalAdded(this, index0, index1);
            return true;
        }
        return false;
    }
    
    @Override
    public boolean removeAll(Collection<?> c)
    {
        boolean changed = false;
        for(Object elem : c) {
            changed |= remove(elem);
        }
        return changed;
    }
    
    @Override
    public boolean retainAll(Collection<?> c)
    {
        boolean changed = false;
        for(E elem : this) {
            if(contains(elem)) {
                changed |= remove(elem);
            }
        }
        return changed;
    }
    
    @Override
    public void clear()
    {
        int index1 = getSize();
        underlyingList.clear();
        fireIntervalRemoved(this, 0, index1);
    }
    
    @Override
    public E get(int index)
    {
        return underlyingList.get(index);
    }
    
    @Override
    public E set(int index, E element)
    {
        underlyingList.set(index, element);
        fireContentsChanged(this, index, index + 1);
        return null;
    }
    
    @Override
    public void add(int index, E element)
    {
        underlyingList.add(index, element);
        fireIntervalAdded(this, index, index + 1);
    }
    
    @Override
    public E remove(int index)
    {
        underlyingList.remove(index);
        fireIntervalRemoved(this, index, index + 1);
        return null;
    }
    
    @Override
    public int indexOf(Object o)
    {
        return underlyingList.indexOf(o);
    }
    
    @Override
    public int lastIndexOf(Object o)
    {
        return underlyingList.lastIndexOf(o);
    }
    
    @Override
    public ListIterator<E> listIterator()
    {
        return underlyingList.listIterator();
    }
    
    @Override
    public ListIterator<E> listIterator(int index)
    {
        return underlyingList.listIterator();
    }
    
    @Override
    public List<E> subList(int fromIndex, int toIndex)
    {
        return new ForwardingListModel<E>(underlyingList.subList(fromIndex, toIndex));
    }
    
}
